﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vowei.Core.Models;
using System.Text.RegularExpressions;
using Vowei.Core;
using Vowei.Data.Models;
using System.Data;
using System.Data.Common;
using System.Data.Objects;

namespace Vowei.Data.Helpers
{
    public static class EntityHelpers
    {
        public static void FillDefaultProperties<T>(this T tobeEditing) 
            where T : ISupportDefaultProperties, INamedTable
        {
            if (tobeEditing == null)
                throw new ArgumentException("tobeEdit");

            tobeEditing.ID = Guid.NewGuid();
            tobeEditing.OpenDate = DateTime.Now;
            tobeEditing.LastModified = DateTime.Now;
        }

        private static Regex _userEmailReg = new Regex(@"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b", RegexOptions.IgnoreCase);
        public static bool MatchUserEmail(string email)
        {
            if (string.IsNullOrEmpty(email))
                return false;
            else
                return _userEmailReg.IsMatch(email);
        }

        /// <summary>
        /// 获取一个任务或者需求的父级需求
        /// </summary>
        /// <param name="ticket">一个任务或者需求</param>
        /// <returns>
        /// 指定任务或者需求的父级需求，如果<paramref name="ticket"/>已经是
        /// 最上层的需求，那么返回null
        /// </returns>
        public static IRequirement GetUpRequirement(this ITicket ticket, VoweiContext context)
        {
            if (ticket == null)
                throw new NullReferenceException();

            // 最上层的需求,上面就是里程碑了
            return context.Requirements.Include("Milestone").SingleOrDefault(r => r.Children.Any(t => t.ID == ticket.ID));
        }

        /// <summary>
        /// 获取一个任务或者需求所属的最下级的里程碑
        /// </summary>
        /// <param name="ticket">一个任务或者需求</param>
        /// <returns>返回一个任务或者需求所属的最下级的里程碑</returns>
        public static IMilestone GetBelongingMilestone(this ITicket ticket, VoweiContext context)
        {
            if (ticket == null)
                throw new NullReferenceException();

            if (ticket is Requirement)
                return (ticket as Requirement).Milestone;
            else
            {
                var req = ticket.GetUpRequirement(context);
                if (req == null)
                    throw new InvalidOperationException(
                        string.Format("工作任务[{0}-{1}],并没有跟任何里程碑或者项目关联起来!", ticket.Title, ticket.ID));
                else
                    return req.Milestone;
            }
        }

        public static IProject GetBelongingProject(this ITicket ticket, VoweiContext context)
        {
            if (ticket == null)
                throw new NullReferenceException();

            var milestone = ticket.GetBelongingMilestone(context);
            return milestone.GetBelongingProject(context);
        }

        public static IProject GetBelongingProject(this IMilestone milestone, VoweiContext context)
        {
            if (milestone == null)
                throw new NullReferenceException();

            milestone = context.Milestones.Include("Parent").First(m => m.ID == milestone.ID);
            while (milestone.Parent != null)
            {
                milestone = context.Milestones.Include("Parent").First(m => m.ID == milestone.Parent.ID);
            }

            return milestone as IProject;
        }

        public static string GetEntityTypeName(Type type)
        {
            var nsName = type.Namespace;

            return string.CompareOrdinal(nsName, "System.Data.Entity.DynamicProxies") == 0 ?
                type.BaseType.Name : type.Name;
        }

        public static bool IsEntityProxyType(Type type)
        {
            return string.CompareOrdinal(type.Namespace, "System.Data.Entity.DynamicProxies") == 0;
        }

        public static IQueryable<IMilestone> AllMilestones(this IMilestone root, VoweiContext context)
        {
            return FullTreeImpl<Milestone, IMilestone>((Milestone)root, context).AsQueryable();
        }

        public static IQueryable<ITicket> AllTickets(this IRequirement root, VoweiContext context)
        {
            return FullTreeImpl<Requirement, ITicket>((Requirement)root, context).AsQueryable();
        }

        public static IQueryable<IRisk> AllRisks(this IProject root, VoweiContext context)
        {
            if (root == null)
                throw new NullReferenceException("root");
            if (context == null)
                throw new ArgumentNullException("context");

            return from r in context.Risks
                   where r.AffectedProjects.Any(p => p.ID == root.ID)
                   select r;
        }

        public static IQueryable<IRequirement> AllRequirements(this IMilestone root, VoweiContext context)
        {
            if (root == null)
                throw new NullReferenceException("root");
            if (context == null)
                throw new ArgumentNullException("context");

            var result = new List<IRequirement>();
            // 添加上当前顶级里程碑中自己里面的任务列表
            var milestones = new List<IMilestone>();
            milestones.Add(root);
            milestones.AddRange(root.AllMilestones(context));

            foreach (var milestone in milestones)
            {
                var reqs = context.Requirements.Where(r => r.Milestone.ID == milestone.ID).ToArray();
                foreach (var req in reqs)
                    result.Add(req);
            }

            return result.Distinct().AsQueryable();
        }

        public static IQueryable<ITicket> AllTickets(this IMilestone root, VoweiContext context)
        {
            if (root == null)
                throw new NullReferenceException("root");
            if (context == null)
                throw new ArgumentNullException("context");

            var result = new List<ITicket>();
            // 添加上当前顶级里程碑中自己里面的任务列表
            var milestones = new List<IMilestone>();
            milestones.Add(root);
            milestones.AddRange(root.AllMilestones(context));

            foreach (var milestone in milestones)
            {
                var reqs = context.Requirements.Where(r => r.Milestone.ID == milestone.ID).ToArray();
                foreach (var req in reqs)
                {
                    result.AddRange(req.FullTreeImpl<Requirement, ITicket>(context));
                    result.Add(req);
                }
            }

            return result.Distinct().AsQueryable();
        }

        private static List<U> FullTreeImpl<T, U>(this T root, VoweiContext context)
            where T : class, ITable, IContainer<U>
        {
            if (root == null)
                throw new NullReferenceException("root");
            if (context == null)
                throw new ArgumentNullException("context");

            var stack = new Stack<T>();
            stack.Push(root);
            var result = new List<U>();
            var table = context.FindTableByType<T>();

            while (stack.Count > 0)
            {
                var iter = table.Find(stack.Pop().ID);
                var children = iter.Children.ToArray();
                foreach (var child in children)
                {
                    if (child is T)
                        stack.Push(child as T);
                }

                result.AddRange(children);
            }

            return result;
        }

        public static ITable[] Ancestors(this ITicket ticket, VoweiContext context)
        {
            if (ticket == null)
                throw new NullReferenceException("ticket");
            if (context == null)
                throw new ArgumentNullException("context");

            var result = new List<ITable>();
            var requirement = ticket.GetUpRequirement(context);

            if (requirement == null)
            {
                throw new InvalidOperationException(
                    string.Format("工作任务[{0}-{1}],并没有跟任何需求关联起来!", ticket.Title, ticket.ID));
            }

            result.Add(requirement);
            var milestone = context.Milestones.Include("Parent").Single(m => m.ID == requirement.Milestone.ID);
            if ( milestone == null )
                throw new InvalidOperationException(
                    string.Format("工作任务[{0}-{1}],并没有跟任何里程碑或者项目关联起来!", requirement.Title, requirement.ID));

            do
            {
                result.Add(milestone);
                milestone = milestone.Parent;
            } while (milestone != null);

            return result.ToArray();
        }

        // TODO: 这个函数应该写成一个存储过程
        public static bool CheckPermission(this IQueryable<Ace> acl, string[] roles, string permission)
        {
            if (acl == null)
                throw new NullReferenceException("acl");
            if (roles == null)
                throw new ArgumentNullException("roles");
            if (string.IsNullOrEmpty(permission))
                throw new ArgumentNullException("permission");
            if (roles.Length == 0)
                return false;

            foreach (var role in roles)
            {
                var aces = acl.Where(a => a.RoleName == role).ToArray();
                foreach (var ace in aces)
                {
                    foreach (var perm in ace.Permissions)
                    {
                        if (perm.Authorized(permission))
                            return true;
                    }
                }
            }

            return false;
        }

    }
}
